home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 4
/
Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso
/
Development
/
Source
/
tarsrc Folder
/
tape.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-02
|
14KB
|
656 lines
#include "tar.h"
#include <Scsi.h>
#include "tape.h"
extern void WPrintf();
#define F_OPEN 0x01
#define B_READ 0x01
#define B_WRITE 0x02
#define RETRYCMD 1 /* Fake error for retry on unit attention */
Boolean tapeDebug = false;
static Boolean lastWasWrite = false;
static Boolean fileMarkRead = false;
static Boolean noRewind = false;
static Boolean readOnly = true;
static Boolean fixed = true;
static Boolean unitAttnSeen = false;
static int flags = 0;
static int blockLen;
#define TV pref.tapeVars
void
TapeDefaults(TapeVars *tv)
{
tv->unit = 5;
tv->minTO = 4;
tv->motionTO = 10;
tv->rewindTO = 180;
tv->forceVariable = false;
tv->forceModeSelect = false;
tv->densityCode = 0;
tv->speed = 0;
tv->bufMode = 0;
}
void
TapeBuildCmd(Byte *cmdBuf, Byte op, Byte two, int n)
{
cmdBuf[0] = op;
cmdBuf[1] = two;
cmdBuf[2] = (n >> 16) & 0xff;
cmdBuf[3] = (n >> 8) & 0xff;
cmdBuf[4] = n & 0xff;
cmdBuf[5] = 0;
}
static char *keyStr[] = {
"NO SENSE", "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR", "HARDWARE ERROR",
"ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT", "BLANK CHECK",
"VENDOR UNIQUE", "COPY ABORTED", "ABORTED COMMAND", "EQUAL", "VOLUME OVERFLOW",
"MISCOMPARE", "RESERVED"
};
PrintSense(Sense *s)
{
WPrintf(" valid %d class %d code %d", s->valid, s->class,
s->code);
WPrintf(" segment %d fmk %d eom %d ili %d key %x %s", s->segment,
s->fmk, s->eom, s->ili, s->key, keyStr[s->key]);
WPrintf(" info %x %x %x %x asl %d", s->info[0],
s->info[1], s->info[2], s->info[3], s->asl);
WPrintf(" asc %x ascq %x sks %x %x %x", s->asc, s->ascq, s->sks[0],
s->sks[1], s->sks[2]);
}
OSErr
TapeCheckSense(Sense *s)
{
switch (s->key) {
case S_NOSENSE:
fileMarkRead = s->fmk;
if (s->eom) {
GenericAlert("\pEnd of tape!");
return(dskFulErr);
}
/* residual = BYTES4_INT(s->info) * (blockLen * fixed); */
return(noErr);
case S_MEDERROR:
GenericAlert("\pUnrecovered medium error!");
return(ioErr);
case S_HDERROR:
GenericAlert("\pHardware error!");
return(ioErr);
case S_VOLOVER:
GenericAlert("\pTape full!");
return(ioErr);
case S_DATAPROTECT:
GenericAlert("\pTape is write protected!");
return(wPrErr);
case S_NODATA:
GenericAlert("\pNo data - blank tape?");
return(ioErr);
case S_UNITATTN:
if (unitAttnSeen)
return(ioErr);
unitAttnSeen = true;
return(RETRYCMD);
default:
WPrintf("tape: SCSI unhandled sense:");
PrintSense(s);
return(ioErr);
break;
}
return(noErr);
}
#define PHWT 0
#define TICKS (*(unsigned int *)0x16A)
int
ScsiCmd(Ptr cmdBuf, int cmdLen, Ptr dataBuf, int dataLen, int *dataSent, int flags, int timeout)
{
int cnt;
unsigned int maxtick, t;
short stat, msg;
short bus;
int ret = 0;
OSErr err;
SCSIInstr si[4];
maxtick = TICKS + timeout * 60;
while (1) {
for (cnt = 0; cnt < 4; cnt++) {
if ((err = SCSIGet()) == noErr)
goto dosel;
WPrintf("SCSIGet = %d %04x", err, SCSIStat());
}
cnt = 0;
while ((bus = SCSIStat()) & 0x40) {
if ((cnt++ & 511) == 0)
WPrintf("stat %04x", bus);
if ((TICKS > maxtick) || (cnt >= 0x40000)) {
WPrintf("TapeCmd timeout free wait phase %04x cnt %d", bus, cnt);
if ((err = SCSIComplete(&stat, &msg, 4)) != noErr) {
WPrintf("SCSIComplete = %d", err);
}
return(-1);
}
}
}
dosel:
if ((err = SCSISelect(TV.unit)) != noErr) {
WPrintf("SCSISelect = %d st %04x", err, SCSIStat());
return(-2);
}
#if PHWT
cnt = 0;
while (1) {
bus = SCSIStat();
cnt++;
if (TICKS > maxtick) {
WPrintf("TapeCmd timeout cmd phase %04x cnt %d", bus, cnt);
if ((err = SCSIComplete(&stat, &msg, 4)) != noErr) {
WPrintf("SCSIComplete = %d", err);
}
return(ioErr);
}
if ((bus & 0x7c) == 0x68)
break;
}
#endif
if ((err = SCSICmd(cmdBuf, cmdLen)) != noErr) {
WPrintf("SCSICmd = %d st %04x", err, SCSIStat());
ret = -3;
goto docomplete;
}
if (flags & (B_READ | B_WRITE)) {
*dataSent = 0;
if (dataLen <= blockLen) {
si[0].scOpcode = scInc;
si[0].scParam1 = dataBuf;
si[0].scParam2 = dataLen;
si[1].scOpcode = scStop;
si[1].scParam1 = 0;
si[1].scParam2 = 0;
} else {
si[0].scOpcode = scInc;
si[0].scParam1 = dataBuf;
si[0].scParam2 = blockLen;
si[1].scOpcode = scLoop;
si[1].scParam1 = -sizeof(SCSIInstr);
si[1].scParam2 = dataLen / blockLen;
si[2].scOpcode = scStop;
si[2].scParam1 = 0;
si[2].scParam2 = 0;
}
#if PHWT
cnt = 0;
while (1) {
bus = SCSIStat();
if ((cnt++ & 511) == 0)
WPrintf("stat %04x", bus);
if (TICKS > maxtick) {
WPrintf("TapeCmd timeout data phase %04x cnt %d", bus, cnt);
if ((err = SCSIComplete(&stat, &msg, 4)) != noErr) {
WPrintf("SCSIComplete = %d", err);
}
return(ioErr);
}
if ((bus & 0x78) == 0x60)
break;
}
#endif
if (flags & B_READ) {
if ((err = SCSIRead((Ptr) si)) != noErr) {
WPrintf("SCSIRead = %d st %04x", err, SCSIStat());
ret = -4;
}
} else {
if ((err = SCSIWrite((Ptr) si)) != noErr) {
WPrintf("SCSIWrite = %d st %04x", err, SCSIStat());
ret = -4;
}
}
*dataSent = si[0].scParam1 - dataBuf;
}
docomplete:
if ((t = maxtick - TICKS) < 60)
t = 60;
if ((err = SCSIComplete(&stat, &msg, t)) != noErr) {
WPrintf("SCSIComplete = %d st %04x", err, SCSIStat());
return((ret != 0) ? ret : -5);
}
return(stat);
}
OSErr
TapeCmd(Ptr cmdBuf, int cmdLen, Ptr dataBuf, int dataLen, int *nMoved, int flags, int timeout)
{
int stat;
OSErr err;
do {
err = noErr;
unitAttnSeen = false;
stat = ScsiCmd(cmdBuf, cmdLen, dataBuf, dataLen, nMoved, flags, timeout);
if (stat) {
int senseMoved;
Sense s;
Byte senseCmd[6] = {OP_SENSE, 0, 0, 0, sizeof(s), 0};
Byte *c = (Byte *) cmdBuf;
WPrintf("TapeCmd: cmd %x %x %x %x %x %x stat %x",
c[0], c[1], c[2], c[3], c[4], c[5], stat);
if (stat = ScsiCmd(senseCmd, sizeof(senseCmd), (Ptr) &s, sizeof(s),
&senseMoved, B_READ, TV.minTO)) {
char buf[64];
sprintf(&buf[1], "Could not get status for failed command %d",
*(Byte *) cmdBuf);
buf[0] = strlen(&buf[1]);
GenericAlert(buf);
err = ioErr;
} else {
err = TapeCheckSense(&s);
}
}
} while (err == RETRYCMD);
return(err);
}
OSErr
TapeOpen(Boolean noRwd, Boolean rdOnly)
{
int t;
int minBlockLen, maxBlockLen;
int stat;
Sense s;
Inquiry inquiry;
Byte cmd[6];
RBLData rbl;
Byte modeCmd[6];
ModeData modeData;
char buf[256];
if (flags & F_OPEN) {
GenericAlert("\pTapeOpen called when already open!");
return(fBsyErr);
}
lastWasWrite = false;
fileMarkRead = false;
noRewind = noRwd;
readOnly = rdOnly;
blockLen = 512; /* Must be non-zero before any commands! */
do {
s.key = S_NOTREADY;
memset(&s, 0, sizeof(s));
TapeBuildCmd(cmd, OP_READY, 0, 0);
if ((stat = ScsiCmd(cmd, sizeof(cmd), NULL, 0, &t, 0, TV.minTO)) == 0)
break;
if (stat < 0) {
if (StopGoAlert("\pTape drive not responding"))
return(nsDrvErr);
continue;
}
TapeBuildCmd(cmd, OP_SENSE, 0, sizeof(s));
if (ScsiCmd(cmd, sizeof(cmd), (Ptr) &s, sizeof(s), &t, B_READ, TV.minTO)) {
if (StopGoAlert("\pCould not read SENSE data"));
return(nsDrvErr);
continue;
}
PrintSense(&s);
if ((s.class != 7) || (s.code != 0)) {
GenericAlert("\pUnknown sense data format");
return(nsDrvErr);
}
switch (s.key) {
case S_ILLREQ:
/* Hmm, a dumb drive. */
WPrintf("Dumb drive");
s.key = S_NOSENSE;
break;
case S_NOTREADY:
if (StopGoAlert("\pDrive not ready"))
return(offLinErr);
break;
case S_NOSENSE:
case S_UNITATTN:
break;
default:
sprintf(&buf[1], "TapeOpen unhandled sense %x", s.key);
buf[0] = strlen(&buf[1]);
GenericAlert(buf);
return(nsDrvErr);
}
} while (s.key != S_NOSENSE);
TapeBuildCmd(cmd, OP_INQUIRY, 0, sizeof(inquiry));
if (
TapeCmd(cmd, sizeof(cmd), (Ptr) &inquiry, sizeof(inquiry), &t, B_READ, TV.minTO) ||
(t != sizeof(inquiry))
) {
GenericAlert("\pCould not read inquiry data");
return(nsDrvErr);
}
if ((inquiry.type != 1) && (inquiry.rmb != 1)) {
sprintf(&buf[1], "SCSI ID %d is not a tape!", TV.unit);
buf[0] = strlen(&buf[1]);
GenericAlert(buf);
return(nsDrvErr);
}
TapeBuildCmd(cmd, OP_RBL, 0, 0);
if (
TapeCmd(cmd, sizeof(cmd), (Ptr) &rbl, sizeof(rbl), &t, B_READ, TV.minTO) ||
(t != sizeof(rbl))
) {
WPrintf("TapeOpen: RBL failed");
/* Hmm, assume fixed 512 byte blocks, see if we get anywhere. */
minBlockLen = maxBlockLen = 512;
} else {
minBlockLen = BYTES2_INT(rbl.minBL);
maxBlockLen = BYTES3_INT(rbl.maxBL);
}
modeCmd[0] = OP_MODESENSE;
modeCmd[1] = modeCmd[2] = modeCmd[3] = modeCmd[5] = 0;
modeCmd[4] = sizeof(modeData);
if (TapeCmd(modeCmd, sizeof(modeCmd), (Ptr) &modeData, sizeof(modeData), &t, B_READ,
TV.minTO)) {
if (tapeDebug)
WPrintf("TapeOpen: MODE SENSE failed");
return(nsDrvErr);
}
/*
WPrintf("TapeOpen: mode sense %x %x wp %d bm %d sp %d bdl %d",
modeData.mdl, modeData.mediumType, modeData.wp, modeData.bufMode,
modeData.speed, modeData.bdl);
WPrintf(" den %x nb %x %x %x bl %x %x %x", modeData.densityCode,
modeData.numBlocks[0], modeData.numBlocks[1], modeData.numBlocks[2],
modeData.blockLen[0], modeData.blockLen[1], modeData.blockLen[2]);
*/
if (!rdOnly && modeData.wp) {
GenericAlert("\pTape is write protected!");
return(wPrErr);
}
blockLen = BYTES3_INT(modeData.blockLen);
/*WPrintf("Block lengths: min %d max %d cur %d\n", minBlockLen, maxBlockLen, blockLen);*/
if ((blockLen == 0) || TV.forceVariable) {
/*
* Drive is in variable block mode
*/
if ((pref.blockSize < minBlockLen) || (pref.blockSize > maxBlockLen)) {
sprintf(&buf[1], "Block size (%d) out drive's range (%d to %d)!",
pref.blockSize, minBlockLen, maxBlockLen);
buf[0] = strlen(&buf[1]);
GenericAlert(buf);
return(nsDrvErr);
}
fixed = false;
blockLen = 512; /* Fake block length */
} else {
if ((pref.blockSize % blockLen) != 0) {
sprintf(&buf[1], "Tar block size (%d) != multiple of tape size (%d)!",
pref.blockSize, blockLen);
buf[0] = strlen(&buf[1]);
GenericAlert(buf);
return(nsDrvErr);
}
}
if (TV.forceVariable || TV.forceModeSelect) {
modeCmd[0] = OP_MODESELECT;
if (!TV.forceModeSelect) {
memset(&modeData, 0, sizeof(modeData));
modeData.bufMode = TV.bufMode;
modeData.speed = TV.speed;
modeData.bdl = 8;
modeData.densityCode = TV.densityCode;
}
if (TV.forceVariable) {
modeData.blockLen[0] =
modeData.blockLen[1] =
modeData.blockLen[2] = 0;
} else {
modeData.blockLen[0] = (blockLen >> 16) & 0xff;
modeData.blockLen[1] = (blockLen >> 8) & 0xff;
modeData.blockLen[2] = blockLen & 0xff;
}
if (TapeCmd(modeCmd, sizeof(modeCmd), (Ptr) &modeData, sizeof(modeData), &t,
B_WRITE, TV.minTO)) {
GenericAlert("\pCould not send mode select to drive!");
return(nsDrvErr);
}
}
flags = F_OPEN;
return(noErr);
}
void
TapeClose()
{
int n;
Byte cmd[6];
if (flags & F_OPEN) {
flags = 0;
if (lastWasWrite) {
n = noRewind ? 1 : 2;
TapeBuildCmd(cmd, OP_WFM, 0, n);
(void) TapeCmd(cmd, sizeof(cmd), NULL, 0, NULL, 0, TV.motionTO);
}
if (!noRewind) {
TapeBuildCmd(cmd, OP_REWIND, IMMEDIATE, 0);
(void) TapeCmd(cmd, sizeof(cmd), NULL, 0, NULL, 0, TV.rewindTO);
}
}
}
int
TapeStrategy(char *buf, int len, Boolean wrt)
{
int blocks, tout;
int op;
int flags;
int nMoved;
OSErr err;
Byte cmd[6];
if (len & (blockLen - 1)) {
return(ioErr);
}
if (wrt) {
op = OP_WRITE;
flags = B_WRITE;
lastWasWrite = true;
} else {
op = OP_READ;
flags = B_READ;
lastWasWrite = false;
}
if (fixed) {
blocks = len;
tout = blocks /= blockLen;
TapeBuildCmd(cmd, op, FIXED, blocks);
} else {
tout = len / blockLen;
TapeBuildCmd(cmd, op, VARIABLE, len);
}
if ((err = TapeCmd(cmd, sizeof(cmd), buf, len, &nMoved, flags, TV.motionTO + tout))
!= noErr) {
char buf[64];
sprintf(&buf[1], "Tape %s error %d", (flags == B_READ) ? "read" : "write", err);
buf[0] = strlen(&buf[1]);
GenericAlert(buf);
return(err);
}
return(nMoved);
}
int
TapeRead(char *buf, int len)
{
if (flags & F_OPEN) {
if (fileMarkRead) {
fileMarkRead = false;
return(0);
}
return(TapeStrategy(buf, len, false));
}
return(notOpenErr);
}
int
TapeWrite(char *buf, int len)
{
if (flags & F_OPEN)
return(TapeStrategy(buf, len, true));
return(notOpenErr);
}
#ifdef notdef
int
TapeIoctl(dev, cmd, addr, mode)
dev_t dev;
int cmd;
caddr_t addr;
int mode;
{
int ret;
int timeout;
struct mtop *mtop;
Byte cmd[6];
if (!(flags & F_OPEN))
return(ENXIO);
switch (cmd) {
case MTIOCTOP:
mtop = (struct mtop *)addr;
switch (mtop->mt_op) {
case MTWEOF:
TapeBuildCmd(cmd, OP_WFM, 0, mtop->mt_count);
timeout = TV.motionTO;
break;
case MTFSF:
TapeBuildCmd(cmd, OP_SPACE, FILEMARKS, mtop->mt_count);
timeout = TV.rewindTO;
break;
case MTFSR:
TapeBuildCmd(cmd, OP_SPACE, BLOCKS, mtop->mt_count);
timeout = TV.rewindTO;
break;
case MTBSF:
TapeBuildCmd(cmd, OP_SPACE, EOD, 0);
timeout = TV.rewindTO;
break;
case MTREW:
TapeBuildCmd(cmd, OP_REWIND, 0, 0);
timeout = TV.rewindTO;
break;
case MTOFFL:
TapeBuildCmd(cmd, OP_REWIND, IMMEDIATE, 0);
timeout = TV.rewindTO;
break;
case MTRETEN:
TapeBuildCmd(cmd, OP_PREWIND, 0, 0);
timeout = TV.rewindTO * 2;
break;
case MTFORMAT:
TapeBuildCmd(cmd, OP_ERASE, 1, 0);
timeout = TV.rewindTO * 2;
break;
default:
return(EINVAL);
}
ret = TapeCmd(cmd, sizeof(cmd), NULL, 0, NULL, 0, timeout);
break;
default:
ret = EINVAL;
break;
}
return(ret);
}
#endif